/**
 * 作者：7years
 * 创建时间：Thu Nov 21 2024 10:29:29 GMT+0800 (中国标准时间)
 *
 * UiBase 界面或弹窗的描述
 */

/**
 * UI组件的基类
 */

import { Component, Node, _decorator } from "cc";
import { IBindInfo, TBindComp, TQuickBindExt } from "../uitls/Decorator";
const { ccclass, property } = _decorator;

export interface IUIArgs {

}

@ccclass('UIBase')
export class UIBase extends Component {

    private _uiArgs?: IUIArgs;

    /**
    * 在节点首次激活时触发，比如所在的场景被载入，或者所在节点被激活的情况下。
    * 在 onLoad 阶段，保证了你可以获取到场景中的其他节点，以及节点关联的资源数据。
    * 生命周期函数顺序：onLoad => onEnable => start => update => lateUpdate
    */
    protected onLoad(): void {
        this._quickBind();
        this.bindWidgets();
    }

    /**
    * 当组件的 enabled 属性从 false 变为 true 时，或者所在节点的 active 属性从 false 变为 true 时，会激活 onEnable 回调。
    * 倘若节点第一次被创建且 enabled 为 true，则会在 onLoad 之后，start 之前被调用。
    */
    protected onEnable(): void {
        this.addAllMsgs();
    }

    /**
    * 当组件的 enabled 属性从 true 变为 false 时，或者所在节点的 active 属性从 true 变为 false 时，会激活 onDisable 回调。
    */
    protected onDisable(): void {
        this.removeAllMsgs();
    }

    /**
    * 在组件第一次激活前，也就是第一次执行 update 之前触发。
    */
    protected start(): void {
        // todo
    }

    /**
    * 当组件或者所在节点调用了 destroy()，则会调用 onDestroy 回调，并在当帧结束时统一回收组件。
    */
    protected onDestroy(): void {
        this.node.targetOff(this);
        this.unscheduleAllCallbacks();
    }

    /**
     * 绑定所有部件，在onLoad时自动调用，需要子类实现
     */
    protected bindWidgets() {
        // 单节点绑定
        // this.childNode = this.node.findChildByName('childName')!;
        // 多节点绑定
        // const childNameArr = ['childName_1', 'childName_2',];
        // const childMap = this.node.findChildMapByNameArr(childNameArr);
        // this._childNode_1 = childMap.get('childName_1')!;
        // this._childNode_2 = childMap.get('childName_2')!;

        // 单组件绑定
        // this._spr = this.node.findCompByName('childName', Sprite);
        // this._lbl = this.node.findCompByName('childName', Label);
        // 多组件绑定
        //const compTypeMap = new Map<string, TCompConstructor<Component>>([
        //    ['spr', Sprite],
        //    ['lbl', Label],
        //]);
        //const compMap = this.node.findCompMap(compTypeMap);
        //this._spr = compMap.get('spr')! as Sprite;
        //this._lbl = compMap.get('lbl')! as Label;
    }

    /**
     * 组件、节点快速绑定
     * @param carrierNode 挂载代码组件的节点
     * @returns 
     */
    private _quickBind() {
        // 要绑定的节点名
        const nodeNameSet = new Set<string>();
        // 收集继承链上的快捷绑定信息
        const bindMap = new Map<string, IBindInfo>();
        let prototype: UIBase & TQuickBindExt = Object.getPrototypeOf(this);
        while (prototype) {
            if (!(prototype instanceof UIBase)) {
                // 只有UIBase的子类支持快捷绑定
                break;
            }
            if (prototype.hasOwnProperty('_$bindMap$_')) {
                prototype._$bindMap$_!.forEach((bindInfo, propertyName) => {
                    if (bindMap.has(propertyName)) {
                        // 保证子类的属性覆盖父类的属性
                        return;
                    }
                    bindMap.set(propertyName, bindInfo);
                    nodeNameSet.add(bindInfo.nodeName);
                });
            }
            prototype = Object.getPrototypeOf(prototype);
        }
        // 没有快捷绑定信息
        if (nodeNameSet.size === 0) {
            return;
        }
        // 递归查找所有需要绑定的节点
        const childNameArr = Array.from(nodeNameSet);
        const childMap = this.node.findChildMapByNameArr(childNameArr);
        if (childMap.size === 0) {
            return;
        }
        // 绑定到实例变量
        bindMap.forEach((bindInfo, propertyName) => {
            const childNode = childMap.get(bindInfo.nodeName);
            if (!childNode) {
                return;
            }
            if (bindInfo.bindType === Node) {
                // @ts-ignore
                this[propertyName] = childNode;
            } else {
                let bindComp = childNode.getComponent(bindInfo.bindType as TBindComp);
                if (!bindComp && bindInfo.isAutoAddComp) {
                    bindComp = childNode.addComponent(bindInfo.bindType as TBindComp);
                }
                // @ts-ignore
                this[propertyName] = bindComp;
            }
        });
    }

    /**
     * 初始化参数，由 UIMgr 在打开界面时调用
     * @param uiArgs 
     */
    initArgs(uiArgs?: IUIArgs): void {
        this._uiArgs = uiArgs;
    };

    /**
     * 使用 ccc.gamesdk.Message.addMessage 添加所有的监听事件。
     * onEnable 时自动调用，组件失效时自动移除通过 ccc.gamesdk.Message.addMessage 添加的所有事件。
     * 注意：不是用 ccc.gamesdk.Message 添加的事件，需要重载父类 removeAllMsgs 方法，并自己移除。
     */
    protected addAllMsgs() {

    }

    /**
     * 移除所有监听事件
     */
    removeAllMsgs() {
    }

    get uiArgs(): IUIArgs {
        return this._uiArgs ?? {};
    }
}